#include "IO3D.h"

#include <sstream>

#include "../Input/PicParams.h"
#include "../Parallel/ParallelMPI3D.h"
#include "../Field/Field3D.h"
#include "../PhysicalField/PhysicalField.h"
#include "../Species/Species.h"

using namespace std;

IO3D::IO3D(PicParams* params_, ParallelMPI* pmpi_) :
    IO(params_, pmpi_)
{

}

IO3D::~IO3D()
{
}


//create hdf5 file, datespace, dateset and so on
void IO3D::createFieldsGroup()
{
    fieldsGroup.dims_global[2] = params->dims_space_global[2] + 1;
    fieldsGroup.dims_global[1] = params->dims_space_global[1] + 1;
    fieldsGroup.dims_global[0] = params->dims_space_global[0] + 1;
 

    fieldsGroup.ndims_[0] = fieldsGroup.dims_global[0];
    fieldsGroup.ndims_[1] = fieldsGroup.dims_global[1];
    fieldsGroup.ndims_[2] = fieldsGroup.dims_global[2];

    fieldsGroup.offset[0] = 0;
    fieldsGroup.offset[1] = 0;
    fieldsGroup.offset[2] = 0;

    fieldsGroup.stride[0] = 1;
    fieldsGroup.stride[1] = 1;
    fieldsGroup.stride[2] = 1;

    fieldsGroup.block[0] = 1;
    fieldsGroup.block[1] = 1;
    fieldsGroup.block[2] = 1;

    //For attribute
    fieldsGroup.aDims = 3;

    //============ Create fieldsGroup ============================================
    const char* h5_name;
    int data_dims = 3;

    addField(fields->rho_global);
    addField(fields->phi_global);
    addField(fields->Ex_global);
    addField(fields->Ey_global);
    addField(fields->rho_global_avg);
    addField(fields->phi_global_avg);
    addField(fields->Ex_global_avg);
    addField(fields->Ey_global_avg);
    for(int i = 0; i < fields->rho_s.size(); i++)
    {
        addField(fields->rho_s_global[i]);
        addField(fields->rho_s_global_avg[i]);
        addField(fields->Vx_s_global_avg[i]);
        addField(fields->Vy_s_global_avg[i]);
        addField(fields->Vz_s_global_avg[i]);
        addField(fields->Vp_s_global_avg[i]);
        addField(fields->T_s_global_avg[i]);

    }
    fieldsGroup.dataset_id.resize( fieldsGroup.dataset_stringName.size() );

}



void IO3D::createDiagsGroup()
{
    Diagnostic3D* diag3D = static_cast<Diagnostic3D*>(diag);

    string diag_name;
    const char* h5_name;
    hid_t dataset_id;
    int n_dim_data = 3;

    diagsGroup.dataset_stringName.resize(0);

    //======= set dimension, offset, stride, and block =================
    diagsGroup.dims_global[2] = params->dims_space_global[2] + 1;
    diagsGroup.dims_global[1] = params->dims_space_global[1] + 1;
    diagsGroup.dims_global[0] = params->dims_space_global[0] + 1;
 

    diagsGroup.ndims_[0] = diagsGroup.dims_global[0];
    diagsGroup.ndims_[1] = diagsGroup.dims_global[1];
    diagsGroup.ndims_[2] = diagsGroup.dims_global[2];

    diagsGroup.offset[0] = 0;
    diagsGroup.offset[1] = 0;
    diagsGroup.offset[2] = 0;

    diagsGroup.stride[0] = 1;
    diagsGroup.stride[1] = 1;
    diagsGroup.stride[2] = 1;

    diagsGroup.block[0] = 1;
    diagsGroup.block[1] = 1;
    diagsGroup.block[2] = 1;

    //======= create diagsGroup ================================
    for(int i_species = 0; i_species < diag3D->n_species; i_species++)
    {
        diagsGroup.dataset_stringName.push_back(diag3D->particleFlux_global[i_species]->name);
        diagsGroup.dataset_data.push_back(diag3D->particleFlux_global[i_species]->data_);

        diagsGroup.dataset_stringName.push_back(diag3D->heatFlux_global[i_species]->name);
        diagsGroup.dataset_data.push_back(diag3D->heatFlux_global[i_species]->data_);
    }
    diagsGroup.dataset_id.resize( diagsGroup.dataset_stringName.size() );

}



//write potential, rho and so on into hdf5 file every some timesteps
void IO3D::write( PicParams* params_, ParallelMPI* pmpi_, PhysicalField* fields_, vector<Species*>& vecSpecies, Diagnostic* diag, int itime)
{
    const char* h5_name;
    int iDiag;
    int n_dim_data = 3;
    Diagnostic3D* diag3D = static_cast<Diagnostic3D*>(diag);

    if( itime % params->timestep_io == 0 && pmpi->isMaster() )
    {
        string step_output_string;

        step_output = itime / params->timestep_io;
        step_output_string = to_string(step_output);
        
        if(step_output_max >= 10 && step_output_max <100)
        {
            if(step_output < 10)
            {
                step_output_string = "0" + step_output_string;
            }
        }
        else if(step_output_max >= 100 && step_output_max <1000)
        {
            if(step_output < 10)
            {
                step_output_string = "00" + step_output_string;
            }
            else if(step_output<100)
            {
                step_output_string = "0" + step_output_string;
            }
        }
        else if(step_output_max >= 1000)
        {
            log_warning<<"step_output_max is too large, please change the code in IO3D.cpp";
        }

        data_file_name = "data/data" + step_output_string + ".h5";
        data_file_id = H5Fcreate( data_file_name.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);

        //============= write attributes, n_dim: number of dimension ======================
        hid_t attrs_dataspace_id, attrs_id;
        int n_dim = 3;
        hsize_t attrs_dims[1];
        attrs_dims[0] = 1;
        attrs_dataspace_id = H5Screate_simple(1, attrs_dims, NULL);
        attrs_id           = H5Acreate2(data_file_id, "n_dim", H5T_STD_I32BE, attrs_dataspace_id, H5P_DEFAULT, H5P_DEFAULT);
        H5Awrite(attrs_id, H5T_NATIVE_INT, &n_dim);
        H5Sclose(attrs_dataspace_id);
        H5Aclose(attrs_id);

        //=============write fields_============================================
        fieldsGroup.group_id = H5Gcreate(data_file_id, "/Fields", H5P_DEFAULT, H5P_DEFAULT,H5P_DEFAULT);
        for(int i = 0; i < fieldsGroup.dataset_stringName.size(); i++)
        {
            fieldsGroup.dataspace_id = H5Screate_simple(n_dim_data, fieldsGroup.dims_global, NULL);
            h5_name = fieldsGroup.dataset_stringName[i].c_str();
            fieldsGroup.dataset_id[i] = H5Dcreate2(fieldsGroup.group_id, h5_name, H5T_NATIVE_DOUBLE, fieldsGroup.dataspace_id,
                                                    H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
            fieldsGroup.status = H5Dwrite(fieldsGroup.dataset_id[i], H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, fieldsGroup.dataset_data[i]);
            fieldsGroup.status = H5Sclose(fieldsGroup.dataspace_id);
            fieldsGroup.status = H5Dclose(fieldsGroup.dataset_id[i]);
        }
        fieldsGroup.status = H5Gclose(fieldsGroup.group_id);


        //=============write Diagnostics ============================================
        diagsGroup.group_id = H5Gcreate(data_file_id, "/Diagnostic", H5P_DEFAULT, H5P_DEFAULT,H5P_DEFAULT);
        for(int i = 0; i < diagsGroup.dataset_stringName.size(); i++)
        {
            diagsGroup.dataspace_id = H5Screate_simple(n_dim_data, diagsGroup.dims_global, NULL);
            h5_name = diagsGroup.dataset_stringName[i].c_str();
            diagsGroup.dataset_id[i] = H5Dcreate2(fieldsGroup.group_id, h5_name, H5T_NATIVE_DOUBLE, diagsGroup.dataspace_id,
                                                    H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
            diagsGroup.status = H5Dwrite(diagsGroup.dataset_id[i], H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, diagsGroup.dataset_data[i]);
            diagsGroup.status = H5Sclose(diagsGroup.dataspace_id);
            diagsGroup.status = H5Dclose(diagsGroup.dataset_id[i]);
        }
        diagsGroup.status = H5Gclose(diagsGroup.group_id);

        status = H5Fclose(data_file_id);
    }



}


void IO3D::writeGrid(Grid* grid)
{

}


void IO3D::readGrid(Grid* grid)
{
    hid_t       grid_dataspace_id;
    hid_t       grid_dataset_id;
    herr_t      grid_status;
    string      grid_dataset_name;

    int ii;
    int grid_ndim = 3;
    hsize_t grid_dims_global[3];

    Grid3D* grid3D = static_cast<Grid3D*>(grid);
    grid_file_name  = "data/grid.h5";
    grid_file_id    = H5Fopen( grid_file_name.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);

    grid_dims_global[0] = grid3D->globalDims_[0];
    grid_dims_global[1] = grid3D->globalDims_[1];
    grid_dims_global[2] = grid3D->globalDims_[2];


    //=============read grid============================================
    grid_dataset_name = "is_wall";
    grid_dataset_id = H5Dopen2(grid_file_id, grid_dataset_name.c_str(), H5P_DEFAULT);
    grid_status = H5Dread(grid_dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &(grid3D->iswall_global_3D(0,0,0)));
    grid_status = H5Dclose(grid_dataset_id);

    grid_dataset_name = "bndr_type";
    grid_dataset_id = H5Dopen2(grid_file_id, grid_dataset_name.c_str(), H5P_DEFAULT);    
    grid_status = H5Dread(grid_dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &(grid3D->bndr_global_3D(0,0,0)));
    grid_status = H5Dclose(grid_dataset_id);

    grid_dataset_name = "bndr_val";
    grid_dataset_id = H5Dopen2(grid_file_id, grid_dataset_name.c_str(), H5P_DEFAULT);        
    grid_status = H5Dread(grid_dataset_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &(grid3D->bndrVal_global_3D(0,0,0)));
    grid_status = H5Dclose(grid_dataset_id);
    //cout<<"IO3D read bndr_global_3d[0,0,0]  "<<grid3D->bndr_global_3D(0,0,0)<<endl;
    grid_status = H5Fclose(grid_file_id);

}
